#ifndef H_C_MPSC_QUEUE_H
#define H_C_MPSC_QUEUE_H

#include "../typedefine.h"
#ifndef _USING_CLI
#include <atomic>
#include <stdexcept>
#endif

namespace TB
{
	template<typename T>
	class CMPSCQueue
	{
		NON_COPYABLE(CMPSCQueue)
	private:
		class CMPSCQueueNodePool;
		class CMPSCQueueNode
		{
		public:
			CMPSCQueueNode()
			{
			}

			/*volatile*/ CMPSCQueueNode* m_poNext = NULL_PTR; //应该不需要使用volatile
			T m_oData = T();
			CMPSCQueueNodePool* m_poPool = NULL_PTR;
		};
		class CMPSCQueueNodePool
		{
			NON_COPYABLE(CMPSCQueueNodePool)
		public:
			CMPSCQueueNodePool()
			{
			}

			// 非线程安全
			inline CMPSCQueueNode* New()
			{
				CMPSCQueueNode& roNode = m_aNodePool[m_btPoolPopIndex++];
				roNode.m_poPool = this;
				return &roNode;
			}

			// 非线程安全，只能调用一次，然后整个Pool就不可用了
			inline INT8 ReleaseAll()
			{
				INT8 btSub = 64 - m_btPoolPopIndex;
				return m_btRefCount.fetch_sub(btSub, std::memory_order_relaxed) - btSub;
			}

			// 线程安全
			inline INT8 DecreaseRefCount()
			{
				return m_btRefCount.fetch_sub(1, std::memory_order_relaxed) - 1;
			}

			// 线程安全
			inline bool IsEmpty() const
			{
				return m_btPoolPopIndex >= 64;
			}

		private:
			CMPSCQueueNode m_aNodePool[64];
			INT8 m_btPoolPopIndex = 0;
			std::atomic<INT8> m_btRefCount = 64;
		};
		class CMPSCQueueNodeFactory
		{
			NON_COPYABLE(CMPSCQueueNodeFactory)
		public:
			CMPSCQueueNodeFactory()
			{
			}
			~CMPSCQueueNodeFactory()
			{
				if (m_poPool != nullptr && 0 == m_poPool->ReleaseAll())
					SAFE_DELETE(m_poPool);
			}
			// 非线程安全
			CMPSCQueueNode* New()
			{
				if (m_poPool == nullptr)
					m_poPool = new CMPSCQueueNodePool();
				CMPSCQueueNode* poNode = m_poPool->New();
				if (m_poPool->IsEmpty())
					m_poPool = nullptr;
				return poNode;
			}
		private:
			CMPSCQueueNodePool* m_poPool = nullptr;
		};
	public:
		CMPSCQueue()
		{
			m_poHeader = m_oMPSCQueueNodeFactory.New();
#ifdef _USING_CLI
			m_poTail = m_poHeader;
#else
			m_oTail.store(m_poHeader, std::memory_order_release);
#endif
		}

#ifndef _USING_CLI
		~CMPSCQueue()
		{
			for (CMPSCQueueNode* p = m_poHeader; p != NULL_PTR;)
			{
				CMPSCQueueNode* tmp = p;
				p = p->m_poNext;
				_ReleaseNode(tmp);
			}
		}
#endif
		inline BOOLN IsEmpty() QCONST_FUNC
		{
			return m_poHeader->m_poNext == NULL_PTR;
		}

		CONST_REF(T) Front() QCONST_FUNC
		{
			CMPSCQueueNode* poNext = m_poHeader->m_poNext; // serialization-point wrt producers, acquire
			if (poNext == NULL_PTR)
#ifdef _USING_CLI
				throw QNEW System::ArgumentOutOfRangeException();
#else
				throw std::out_of_range("");
#endif
			return poNext->m_oData;
		}

		UINT32 Pop()
		{
			CMPSCQueueNode* poHeader = m_poHeader;
			CMPSCQueueNode* poNext = poHeader->m_poNext; // serialization-point wrt producers, acquire
			if (poNext == NULL_PTR)
#ifdef _USING_CLI
				throw QNEW System::ArgumentOutOfRangeException();
#else
				throw std::out_of_range("");
#endif
			m_poHeader = poNext;
			_ReleaseNode(poHeader);
			return (UINT32)m_dwCount.fetch_sub(1, std::memory_order_release);
		}

		inline UINT32 Push(CONST_REF(T) roData)
		{
			UINT32 dwCount = (UINT32)m_dwCount.fetch_add(1, std::memory_order_acquire);
			CMPSCQueueNode* poNode = m_oMPSCQueueNodeFactory.New();
			poNode->m_oData = roData;
			CMPSCQueueNode* prev = m_oTail.exchange(poNode, std::memory_order_acq_rel); // serialization-point wrt producers, acquire-release
			prev->m_poNext = poNode; // serialization-point wrt consumer, release
			return dwCount;
		}

		inline UINT32 GetCount()
		{
			return (UINT32)m_dwCount.load(std::memory_order_acquire);
		}

	private:
		void _ReleaseNode(CMPSCQueueNode* poNode)
		{
			if (poNode->m_poPool->DecreaseRefCount() == 0)
				SAFE_DELETE(poNode->m_poPool);
		}

	private:
		CMPSCQueueNode* m_poHeader = NULL_PTR;
		std::atomic<CMPSCQueueNode*> m_oTail = NULL_PTR;
		std::atomic_uint_fast32_t m_dwCount;
		thread_local static CMPSCQueueNodeFactory m_oMPSCQueueNodeFactory;
	};

	template<typename T> thread_local CMPSCQueue<T>::CMPSCQueueNodeFactory CMPSCQueue<T>::m_oMPSCQueueNodeFactory;
}

#endif
